package com.sduept.nwld.dataserver.manager.message;

import com.sduept.bigdata.fault.common.ParamDesc;
import com.sduept.core.ParamManager;
import com.sduept.core.entity.SystemParameter;
import com.sduept.nwld.dataserver.exceptions.MessageException;
import com.sduept.permission.entity.PersonnelEntityBean;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.nio.charset.Charset;
import java.util.*;
import java.util.Map.Entry;

/**
 * eSpace平台消息发送服务
 * 
 * @author Author
 * @version V1.0
 * @Date 2018年7月18日 下午2:34:38
 */
@Service
@Slf4j
public class EspaceMessagePushManager {

	public static final String SPLITSIGN = ",";

	private static final String STATUS_SIGN = "status";

	private static final String STATUS_TRUE = "true";

	@Autowired
	private ParamManager pc;

	private LinkedList<Message> messageList = new LinkedList<>();

	/**
	 * 消息推送失败的重新推送方法
	 */
//	@Schedule(dayOfWeek = "*", hour = "*", minute = "*/10", second = "0", persistent = false) todo springboot
	@Scheduled(cron = "0 */10 * * * ?")
	public void pushFailedMessage() {
		if (!serviceAvailable()) {
			return;
		}
		// 默认消息存储时间24（小时）
		int timeout = 24;
		SystemParameter systemParameter = pc.get(ParamDesc.ECMESSAGE_TIMEOUT);
		if (null != systemParameter && StringUtils.isNotEmpty(systemParameter.getValue())) {
			timeout = Integer.parseInt(systemParameter.getValue());
		}
		Iterator<Message> iterator = messageList.iterator();
		while (iterator.hasNext()) {
			Message message = iterator.next();
			long temp = System.currentTimeMillis() - message.getRecordTime().getTime();
			long difference = timeout * 3600 * 1000L;
			if (temp > difference) {
				iterator.remove();
				continue;
			}
			try {
				boolean pushStatus = messagePush(message.getPhone(), message.getUserName(), message.getContent());
				if (!pushStatus) {
					break;
				}
				iterator.remove();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * 多人推送相同的消息
	 * 
	 * @param persons
	 * @param content
	 * @throws Exception
	 */
	public boolean messagePush(List<PersonnelEntityBean> persons, String content) throws MessageException {
		if (!serviceAvailable()) {
			return true;
		}
		StringBuilder temPhone = new StringBuilder();
		StringBuilder temUserName = new StringBuilder();
		for (PersonnelEntityBean person : persons) {
			if (StringUtils.isEmpty(person.getTelephone())) {
				continue;
			}
			temPhone.append(person.getTelephone() + SPLITSIGN);
			temUserName.append(person.getName() + SPLITSIGN);
		}
		String phone = temPhone.toString();
		phone = phone.substring(0, phone.lastIndexOf(SPLITSIGN));
		String userName = temUserName.toString();
		userName = userName.substring(0, userName.lastIndexOf(SPLITSIGN));
		return messagePush(phone, userName, content);
	}

	/**
	 * 多人推送相同的消息
	 * Map<手机号，姓名>
	 * @param personMap
	 * @param content
	 * @return
	 * @throws MessageException
	 */
	public boolean messagePush(Map<String, String> personMap, String content) throws MessageException {
		if (!serviceAvailable()) {
			return true;
		}
		StringBuilder temPhone = new StringBuilder();
		StringBuilder temUserName = new StringBuilder();
		for (Entry<String, String> entry : personMap.entrySet()) {
			if (StringUtils.isEmpty(entry.getValue())) {
				continue;
			}
			temPhone.append(entry.getKey() + SPLITSIGN);
			temUserName.append(entry.getValue() + SPLITSIGN);
		}
		String phone = temPhone.toString();
		phone = phone.substring(0, phone.lastIndexOf(SPLITSIGN));
		String userName = temUserName.toString();
		userName = userName.substring(0, userName.lastIndexOf(SPLITSIGN));
		return messagePush(phone, userName, content);
	}
	
	/**
	 * 单人消息推送
	 * 
	 * @param phone
	 *            用户手机号码（不能为空），多个手机号以，分割
	 * @param userName
	 *            用户中文名称（不能为空）多个姓名以，分割
	 * @param content
	 *            消息内容
	 * @throws Exception
	 */
	public boolean messagePush(String phone, String userName, String content) throws MessageException {
		if (!serviceAvailable()) {
			return true;
		}
		if (StringUtils.isEmpty(phone)) {
			throw new MessageException("手机号码不能为空");
		}
		if (StringUtils.isEmpty(userName)) {
			throw new MessageException("用户中文名称不能为空");
		}
		String requestBody = generateRequestBody(phone, userName, null, content);
		if (StringUtils.isEmpty(requestBody)) {
			return false;
		}
		SystemParameter parameter = pc.get(ParamDesc.ECMESSAGE_WSDL_URL);
		SystemParameter soapParam = pc.get(ParamDesc.ECMESSAGE_SOAPACTION);
		if (null == parameter || StringUtils.isEmpty(parameter.getValue()) || null == soapParam) {
			return false;
		}
		String responseMsg = null;
		try {
			responseMsg = postSoap(parameter.getValue(), requestBody, soapParam.getValue());
		} catch (IOException e) {
			e.printStackTrace();
		}
		boolean status = parseResponseResult(responseMsg);
		if (!status) {
			// 加入到待发送队列
			Message message = new Message(userName, phone, content, new Date());
			addMessage(message);
			log.warn("消息发送失败：" + message.toString());
		}
		return status;
	}

	/**
	 * 采用HttpClient访问webService，并拿到返回值
	 * 
	 * @param postUrl
	 * @param soapXml
	 * @return
	 */
	private String postSoap(String postUrl, String soapXml, String soapAction) throws IOException {
		StringBuffer result = new StringBuffer();
		HttpPost httpPost = new HttpPost(postUrl);
		StringEntity data = new StringEntity(soapXml, Charset.forName("UTF-8"));
		RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(10 * 1000).setConnectionRequestTimeout(5 * 1000).setConnectTimeout(10 * 1000).build();
		httpPost.setConfig(requestConfig);
		httpPost.setHeader("Content-Type", "text/xml;charset=UTF-8");
		// 根据实际判断是否需要SoapAction
		if (StringUtils.isNotEmpty(soapAction)) {
			httpPost.setHeader("SOAPAction", soapAction);
		}
		httpPost.setEntity(data);
		try (CloseableHttpClient closeableHttpClient = HttpClientBuilder.create().build(); CloseableHttpResponse response = closeableHttpClient.execute(httpPost);) {
			HttpEntity httpEntity = response.getEntity();
			if (httpEntity != null) {
				result.append(EntityUtils.toString(httpEntity, "UTF-8"));
			}
		}
		return result.toString();
	}

	/**
	 * 根据响应结果判断消息是否发送成功
	 * 
	 * @param xmlContent
	 * @return
	 */
	private boolean parseResponseResult(String xmlContent) {
		if (StringUtils.isEmpty(xmlContent)) {
			return false;
		}
		Document document = Jsoup.parse(xmlContent);
		Elements elements = document.select(STATUS_SIGN);
		if (null == elements) {
			return false;
		}
		String resonseStatus = elements.get(0).html();
		if (STATUS_TRUE.equals(resonseStatus)) {
			return true;
		}
		return false;
	}

	/**
	 * 生成请求体
	 * 
	 * @param phone
	 * @param userName
	 * @param content
	 * @return
	 */
	private String generateRequestBody(String phone, String userName, String reAccount, String content) {
		SystemParameter systemParameter = pc.get(ParamDesc.ECMESSAGE_WSDL_REQUEST_HEADER);
		if (null == systemParameter || StringUtils.isEmpty(systemParameter.getValue())) {
			return null;
		}
		String result = systemParameter.getValue().replace("Param1", phone);
		result = result.replace("Param2", userName);
		result = result.replace("Param3", content);
		return result;
	}

	/**
	 * 将消息加入到队列之中
	 * 
	 * @param message
	 */
	private void addMessage(Message message) {
		boolean isExist = false;
		for (Message tmp : messageList) {
			if (message.equals(tmp)) {
				isExist = true;
				break;
			}
		}
		if (!isExist) {
			messageList.add(message);
		}
	}

	/**
	 * 根据系统参数判断服务是否可用 备注： 只有状态为1时，才可以使用
	 * 
	 * @return
	 */
	private boolean serviceAvailable() {
		SystemParameter param = pc.get(ParamDesc.ECMESSAGE_SERVICE_STATUS);
		if (null == param || StringUtils.isEmpty(param.getValue())) {
			return false;
		}
		int temp = Integer.parseInt(param.getValue());
		if (1 == temp) {
			return true;
		}
		return false;
	}

	/**
	 * 存储发送失败的消息
	 * 
	 * @author Author
	 * @version V1.0
	 * @Date 2018年7月19日 上午11:05:07
	 */
	public class Message {
		String userName;
		String phone;
		String content;
		Date recordTime;

		public Message(String userName, String phone, String content, Date recordTime) {
			super();
			this.userName = userName;
			this.phone = phone;
			this.content = content;
			this.recordTime = recordTime;
		}

		public String getUserName() {
			return userName;
		}

		public void setUserName(String userName) {
			this.userName = userName;
		}

		public String getPhone() {
			return phone;
		}

		public void setPhone(String phone) {
			this.phone = phone;
		}

		public String getContent() {
			return content;
		}

		public void setContent(String content) {
			this.content = content;
		}

		public Date getRecordTime() {
			return recordTime;
		}

		public void setRecordTime(Date recordTime) {
			this.recordTime = recordTime;
		}

		@Override
		public boolean equals(Object obj) {
			if (obj == this)
				return true;
			if (!(obj instanceof Message)) {
				return false;
			}
			Message bean = (Message) obj;
			return Objects.equals(userName, bean.userName) && Objects.equals(phone, bean.phone) && Objects.equals(content, bean.content);
		}

		@Override
		public String toString() {
			return "Message [userName=" + userName + ", phone=" + phone + ", content=" + content + ", recordTime=" + recordTime + "]";
		}

		@Override
		public int hashCode() {
			return Objects.hash(userName, phone, content);
		}

	}

}
